home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Windows Game Programming for Dummies (2nd Edition)
/
WinGamProgFD.iso
/
mac
/
DirectX SDK
/
DXSDK
/
samples
/
Multimedia
/
Direct3D
/
PointSprites
/
pointsprites.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
2001-10-31
|
37KB
|
1,114 lines
//-----------------------------------------------------------------------------
// File: PointSprites.cpp
//
// Desc: Sample showing how to use point sprites to do particle effects
//
// Copyright (c) 1997-2001 Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#define STRICT
#include <math.h>
#include <stdio.h>
#include <D3DX8.h>
#include "D3DApp.h"
#include "D3DFont.h"
#include "D3DUtil.h"
#include "DXUtil.h"
#include "resource.h"
// Helper function to stuff a FLOAT into a DWORD argument
inline DWORD FtoDW( FLOAT f ) { return *((DWORD*)&f); }
//-----------------------------------------------------------------------------
// Custom vertex types
//-----------------------------------------------------------------------------
struct COLORVERTEX
{
D3DXVECTOR3 v;
D3DCOLOR color;
FLOAT tu;
FLOAT tv;
};
#define D3DFVF_COLORVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)
struct POINTVERTEX
{
D3DXVECTOR3 v;
D3DCOLOR color;
};
#define D3DFVF_POINTVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE)
//-----------------------------------------------------------------------------
// Global structs and data for the ground object
//-----------------------------------------------------------------------------
#define GROUND_WIDTH 256.0f
#define GROUND_HEIGHT 256.0f
#define GROUND_GRIDSIZE 8
#define GROUND_TILE 32
#define GROUND_COLOR 0xcccccccc
//-----------------------------------------------------------------------------
// Global data for the particles
//-----------------------------------------------------------------------------
struct PARTICLE
{
BOOL m_bSpark; // Sparks are less energetic particles that
// are generated where/when the main particles
// hit the ground
D3DXVECTOR3 m_vPos; // Current position
D3DXVECTOR3 m_vVel; // Current velocity
D3DXVECTOR3 m_vPos0; // Initial position
D3DXVECTOR3 m_vVel0; // Initial velocity
FLOAT m_fTime0; // Time of creation
D3DXCOLOR m_clrDiffuse; // Initial diffuse color
D3DXCOLOR m_clrFade; // Faded diffuse color
FLOAT m_fFade; // Fade progression
PARTICLE* m_pNext; // Next particle in list
};
enum PARTICLE_COLORS { COLOR_WHITE, COLOR_RED, COLOR_GREEN, COLOR_BLUE, NUM_COLORS };
D3DXCOLOR g_clrColor[NUM_COLORS] =
{
D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f ),
D3DXCOLOR( 1.0f, 0.5f, 0.5f, 1.0f ),
D3DXCOLOR( 0.5f, 1.0f, 0.5f, 1.0f ),
D3DXCOLOR( 0.125f, 0.5f, 1.0f, 1.0f )
};
DWORD g_clrColorFade[NUM_COLORS] =
{
D3DXCOLOR( 1.0f, 0.25f, 0.25f, 1.0f ),
D3DXCOLOR( 1.0f, 0.25f, 0.25f, 1.0f ),
D3DXCOLOR( 0.25f, 0.75f, 0.25f, 1.0f ),
D3DXCOLOR( 0.125f, 0.25f, 0.75f, 1.0f )
};
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
class CParticleSystem
{
protected:
FLOAT m_fRadius;
DWORD m_dwBase;
DWORD m_dwFlush;
DWORD m_dwDiscard;
DWORD m_dwParticles;
DWORD m_dwParticlesLim;
PARTICLE* m_pParticles;
PARTICLE* m_pParticlesFree;
// Geometry
LPDIRECT3DVERTEXBUFFER8 m_pVB;
public:
CParticleSystem( DWORD dwFlush, DWORD dwDiscard, FLOAT fRadius );
~CParticleSystem();
HRESULT RestoreDeviceObjects( LPDIRECT3DDEVICE8 pd3dDevice );
HRESULT InvalidateDeviceObjects();
HRESULT Update( FLOAT fSecsPerFrame, DWORD dwNumParticlesToEmit,
const D3DXCOLOR &dwEmitColor, const D3DXCOLOR &dwFadeColor,
FLOAT fEmitVel, D3DXVECTOR3 vPosition );
HRESULT Render( LPDIRECT3DDEVICE8 pd3dDevice );
};
//-----------------------------------------------------------------------------
// Name: class CMyD3DApplication
// Desc: Application class. The base class (CD3DApplication) provides the
// generic functionality needed in all Direct3D samples. CMyD3DApplication
// adds functionality specific to this sample program.
//-----------------------------------------------------------------------------
class CMyD3DApplication : public CD3DApplication
{
CD3DFont* m_pFont;
CD3DFont* m_pFontSmall;
// Ground stuff
LPDIRECT3DTEXTURE8 m_pGroundTexture;
D3DXPLANE m_planeGround;
LPDIRECT3DVERTEXBUFFER8 m_pGroundVB;
LPDIRECT3DINDEXBUFFER8 m_pGroundIB;
DWORD m_dwNumGroundIndices;
DWORD m_dwNumGroundVertices;
// Particle stuff
LPDIRECT3DTEXTURE8 m_pParticleTexture;
CParticleSystem* m_pParticleSystem;
DWORD m_dwNumParticlesToEmit;
DWORD m_dwParticleColor;
BOOL m_bAnimateEmitter;
BYTE m_bKey[256];
BOOL m_bDrawReflection;
BOOL m_bCanDoAlphaBlend;
BOOL m_bDrawHelp;
// Variables for determining view position
D3DXVECTOR3 m_vPosition;
D3DXVECTOR3 m_vVelocity;
FLOAT m_fYaw;
FLOAT m_fYawVelocity;
FLOAT m_fPitch;
FLOAT m_fPitchVelocity;
D3DXMATRIX m_matView;
D3DXMATRIX m_matOrientation;
HRESULT ConfirmDevice( D3DCAPS8*, DWORD, D3DFORMAT );
protected:
HRESULT OneTimeSceneInit();
HRESULT InitDeviceObjects();
HRESULT RestoreDeviceObjects();
HRESULT InvalidateDeviceObjects();
HRESULT DeleteDeviceObjects();
HRESULT Render();
HRESULT FrameMove();
HRESULT FinalCleanup();
VOID UpdateCamera();
public:
LRESULT MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
CMyD3DApplication();
};
//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: Entry point to the program. Initializes everything, and goes into a
// message-processing loop. Idle time is used to render the scene.
//-----------------------------------------------------------------------------
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
CMyD3DApplication d3dApp;
if( FAILED( d3dApp.Create( hInst ) ) )
return 0;
return d3dApp.Run();
}
//-----------------------------------------------------------------------------
// Name: CMyD3DApplication()
// Desc: Application constructor. Sets attributes for the app.
//-----------------------------------------------------------------------------
CMyD3DApplication::CMyD3DApplication()
{
m_strWindowTitle = _T("PointSprites: Using particle effects");
m_bUseDepthBuffer = TRUE;
m_pFont = new CD3DFont( _T("Arial"), 12, D3DFONT_BOLD );
m_pFontSmall = new CD3DFont( _T("Arial"), 9, D3DFONT_BOLD );
m_pParticleSystem = new CParticleSystem( 512, 2048, 0.03f );
m_pParticleTexture = NULL;
m_dwNumParticlesToEmit = 10;
m_bAnimateEmitter = FALSE;
m_dwParticleColor = COLOR_WHITE;
m_pGroundTexture = NULL;
m_dwNumGroundVertices = (GROUND_GRIDSIZE + 1) * (GROUND_GRIDSIZE + 1);
m_dwNumGroundIndices = (GROUND_GRIDSIZE * GROUND_GRIDSIZE) * 6;
m_pGroundVB = NULL;
m_pGroundIB = NULL;
m_planeGround = D3DXPLANE( 0.0f, 1.0f, 0.0f, 0.0f );
m_bDrawReflection = FALSE;
m_bCanDoAlphaBlend = FALSE;
m_bDrawHelp = FALSE;
ZeroMemory( m_bKey, 256 );
m_vPosition = D3DXVECTOR3( 0.0f, 3.0f,-4.0f );
m_vVelocity = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
m_fYaw = 0.03f;
m_fYawVelocity = 0.0f;
m_fPitch = 0.5f;
m_fPitchVelocity = 0.0f;
D3DXMatrixTranslation( &m_matView, 0.0f, 0.0f, 10.0f );
D3DXMatrixTranslation( &m_matOrientation, 0.0f, 0.0f, 0.0f );
}
//-----------------------------------------------------------------------------
// Name: OneTimeSceneInit()
// Desc: Called during initial app startup, this function performs all the
// permanent initialization.
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::OneTimeSceneInit()
{
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: FrameMove()
// Desc: Called once per frame, the call is the entry point for animating
// the scene.
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::FrameMove()
{
// Slow things down for the REF device
if( m_d3dCaps.DeviceType == D3DDEVTYPE_REF )
m_fElapsedTime = 0.05f;
// Determine emitter position
D3DXVECTOR3 vEmitterPostion( 0.0f, 0.0f, 0.f );
if( m_bAnimateEmitter )
vEmitterPostion = D3DXVECTOR3( 3*sinf(m_fTime), 0.0f, 3*cosf(m_fTime) );
// Update particle system
m_pParticleSystem->Update( m_fElapsedTime, m_dwNumParticlesToEmit,
g_clrColor[m_dwParticleColor],
g_clrColorFade[m_dwParticleColor], 8.0f,
vEmitterPostion );
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: Render()
// Desc: Called once per frame, the call is the entry point for 3d
// rendering. This function sets up render states, clears the
// viewport, and renders the scene.
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::Render()
{
// Update the camera here rather than in FrameMove() so you can
// move the camera even when the scene is paused
UpdateCamera();
if( FAILED( m_pd3dDevice->BeginScene() ) )
return S_OK;
// Clear the viewport
m_pd3dDevice->Clear( 0L, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
0x00000000, 1.0f, 0L );
// Draw reflection of particles
if( m_bDrawReflection )
{
D3DXMATRIX matReflectedView;
D3DXMatrixReflect( &matReflectedView, &m_planeGround );
D3DXMatrixMultiply( &matReflectedView, &matReflectedView, &m_matView );
m_pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE );
m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
m_pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
m_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );
m_pd3dDevice->SetTransform( D3DTS_VIEW, &matReflectedView );
m_pd3dDevice->SetTexture( 0, m_pParticleTexture );
m_pParticleSystem->Render( m_pd3dDevice );
m_pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE );
m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
}
// Draw the ground
if( m_bDrawReflection )
{
m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
m_pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
m_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
}
m_pd3dDevice->SetTransform( D3DTS_VIEW, &m_matView );
m_pd3dDevice->SetTexture( 0, m_pGroundTexture );
m_pd3dDevice->SetVertexShader( D3DFVF_COLORVERTEX );
m_pd3dDevice->SetStreamSource( 0, m_pGroundVB, sizeof(COLORVERTEX) );
m_pd3dDevice->SetIndices( m_pGroundIB, 0L );
m_pd3dDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, m_dwNumGroundVertices, 0, m_dwNumGroundIndices/3 );
// Draw particles
m_pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE );
m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
m_pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
m_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );
m_pd3dDevice->SetTexture(0, m_pParticleTexture );
m_pParticleSystem->Render( m_pd3dDevice );
m_pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE );
m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
// Output statistics
m_pFont->DrawText( 2, 0, D3DCOLOR_ARGB(255,255,255,0), m_strFrameStats );
m_pFont->DrawText( 2, 20, D3DCOLOR_ARGB(255,255,255,0), m_strDeviceStats );
// Draw help
if( m_bDrawHelp )
{
m_pFontSmall->DrawText( 2, 40, 0xffffffff,
_T("Keyboard controls:") );
m_pFontSmall->DrawText( 20, 60, 0xffffffff,
_T("Move\nTurn\nPitch\nSlide\n\nHelp\n"
"Change device\nAnimate emitter\n"
"Change color\n\nToggle reflection\n"
"Exit") );
m_pFontSmall->DrawText( 160, 60, 0xffffffff,
_T("W,S\nE,Q\nA,Z\nArrow keys\n\n"
"F1\nF2\nF3\nF4\n\nR\nEsc") );
}
else
m_pFontSmall->DrawText( 2, 40, 0xffffffff,
_T("Press F1 for help") );
// End the scene.
m_pd3dDevice->EndScene();
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: UpdateCamera()
// Desc:
//-----------------------------------------------------------------------------
VOID CMyD3DApplication::UpdateCamera()
{
FLOAT fElapsedTime;
if( m_fElapsedTime > 0.0f )
fElapsedTime = m_fElapsedTime;
else
fElapsedTime = 0.05f;
FLOAT fSpeed = 3.0f*fElapsedTime;
FLOAT fAngularSpeed = 1.0f*fElapsedTime;
// De-accelerate the camera movement (for smooth motion)
m_vVelocity *= 0.9f;
m_fYawVelocity *= 0.9f;
m_fPitchVelocity *= 0.9f;
// Process keyboard input
if( m_bKey[VK_RIGHT] ) m_vVelocity.x += fSpeed; // Slide Right
if( m_bKey[VK_LEFT] ) m_vVelocity.x -= fSpeed; // Slide Left
if( m_bKey[VK_UP] ) m_vVelocity.y += fSpeed; // Slide Up
if( m_bKey[VK_DOWN] ) m_vVelocity.y -= fSpeed; // Slide Down
if( m_bKey['W'] ) m_vVelocity.z += fSpeed; // Move Forward
if( m_bKey['S'] ) m_vVelocity.z -= fSpeed; // Move Backward
if( m_bKey['E'] ) m_fYawVelocity += fSpeed; // Turn Right
if( m_bKey['Q'] ) m_fYawVelocity -= fSpeed; // Turn Left
if( m_bKey['Z'] ) m_fPitchVelocity += fSpeed; // Turn Down
if( m_bKey['A'] ) m_fPitchVelocity -= fSpeed; // Turn Up
if( m_bKey[VK_ADD] ) if( m_dwNumParticlesToEmit < 10 ) m_dwNumParticlesToEmit++;
if( m_bKey[VK_SUBTRACT] ) if( m_dwNumParticlesToEmit > 0 ) m_dwNumParticlesToEmit--;
// Update the position vector
D3DXVECTOR3 vT = m_vVelocity * fSpeed;
D3DXVec3TransformNormal( &vT, &vT, &m_matOrientation );
m_vPosition += vT;
if( m_vPosition.y < 1.0f )
m_vPosition.y = 1.0f;
// Update the yaw-pitch-rotation vector
m_fYaw += fAngularSpeed * m_fYawVelocity;
m_fPitch += fAngularSpeed * m_fPitchVelocity;
if( m_fPitch < 0.0f ) m_fPitch = 0.0f;
if( m_fPitch > D3DX_PI/2 ) m_fPitch = D3DX_PI/2;
// Set the view matrix
D3DXQUATERNION qR;
D3DXQuaternionRotationYawPitchRoll( &qR, m_fYaw, m_fPitch, 0.0f );
D3DXMatrixAffineTransformation( &m_matOrientation, 1.25f, NULL, &qR, &m_vPosition );
D3DXMatrixInverse( &m_matView, NULL, &m_matOrientation );
}
//-----------------------------------------------------------------------------
// Name: InitDeviceObjects()
// Desc: Initialize scene objects.
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::InitDeviceObjects()
{
HRESULT hr;
// Create textures
if( FAILED( D3DUtil_CreateTexture( m_pd3dDevice, _T("Ground2.bmp"),
&m_pGroundTexture ) ) )
return D3DAPPERR_MEDIANOTFOUND;
if( FAILED( D3DUtil_CreateTexture( m_pd3dDevice, _T("Particle.bmp"),
&m_pParticleTexture ) ) )
return D3DAPPERR_MEDIANOTFOUND;
// Set up the fonts and textures
m_pFont->InitDeviceObjects( m_pd3dDevice );
m_pFontSmall->InitDeviceObjects( m_pd3dDevice );
// Check if we can do the reflection effect
m_bCanDoAlphaBlend = (m_d3dCaps.SrcBlendCaps & D3DPBLENDCAPS_SRCALPHA) &&
(m_d3dCaps.DestBlendCaps & D3DPBLENDCAPS_INVSRCALPHA);
if( m_bCanDoAlphaBlend )
m_bDrawReflection = TRUE;
// Create ground object
{
// Create vertex buffer for ground object
hr = m_pd3dDevice->CreateVertexBuffer( m_dwNumGroundVertices*sizeof(COLORVERTEX),
D3DUSAGE_WRITEONLY, D3DFVF_COLORVERTEX,
D3DPOOL_MANAGED, &m_pGroundVB );
if( FAILED(hr) )
return E_FAIL;
// Fill vertex buffer
COLORVERTEX* pVertices;
if( FAILED( m_pGroundVB->Lock( 0, 0, (BYTE**)&pVertices, NULL ) ) )
return hr;
// Fill in vertices
for( DWORD zz = 0; zz <= GROUND_GRIDSIZE; zz++ )
{
for( DWORD xx = 0; xx <= GROUND_GRIDSIZE; xx++ )
{
pVertices->v.x = GROUND_WIDTH * (xx/(FLOAT)GROUND_GRIDSIZE-0.5f);
pVertices->v.y = 0.0f;
pVertices->v.z = GROUND_HEIGHT * (zz/(FLOAT)GROUND_GRIDSIZE-0.5f);
pVertices->color = GROUND_COLOR;
pVertices->tu = xx*GROUND_TILE/(FLOAT)GROUND_GRIDSIZE;
pVertices->tv = zz*GROUND_TILE/(FLOAT)GROUND_GRIDSIZE;
pVertices++;
}
}
m_pGroundVB->Unlock();
// Create the index buffer
WORD* pIndices;
hr = m_pd3dDevice->CreateIndexBuffer( m_dwNumGroundIndices*sizeof(WORD),
D3DUSAGE_WRITEONLY,
D3DFMT_INDEX16, D3DPOOL_MANAGED,
&m_pGroundIB );
if( FAILED(hr) )
return E_FAIL;
// Fill the index buffer
m_pGroundIB->Lock( 0, m_dwNumGroundIndices*sizeof(WORD), (BYTE**)&pIndices, 0 );
if( FAILED(hr) )
return E_FAIL;
// Fill in indices
for( DWORD z = 0; z < GROUND_GRIDSIZE; z++ )
{
for( DWORD x = 0; x < GROUND_GRIDSIZE; x++ )
{
DWORD vtx = x + z * (GROUND_GRIDSIZE+1);
*pIndices++ = (WORD)( vtx + 1 );
*pIndices++ = (WORD)( vtx + 0 );
*pIndices++ = (WORD)( vtx + 0 + (GROUND_GRIDSIZE+1) );
*pIndices++ = (WORD)( vtx + 1 );
*pIndices++ = (WORD)( vtx + 0 + (GROUND_GRIDSIZE+1) );
*pIndices++ = (WORD)( vtx + 1 + (GROUND_GRIDSIZE+1) );
}
}
m_pGroundIB->Unlock();
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: RestoreDeviceObjects()
// Desc:
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::RestoreDeviceObjects()
{
HRESULT hr;
m_pFont->RestoreDeviceObjects();
m_pFontSmall->RestoreDeviceObjects();
// Set the world matrix
D3DXMATRIX matWorld;
D3DXMatrixIdentity( &matWorld );
m_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );
// Set projection matrix
D3DXMATRIX matProj;
FLOAT fAspect = ((FLOAT)m_d3dsdBackBuffer.Width) / m_d3dsdBackBuffer.Height;
D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, fAspect, 0.1f, 100.0f );
m_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
m_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_DISABLE );
m_pd3dDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
m_pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
m_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );
m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );
m_pd3dDevice->SetRenderState( D3DRS_SHADEMODE, D3DSHADE_FLAT );
// Initialize the particle system
if( FAILED( hr = m_pParticleSystem->RestoreDeviceObjects( m_pd3dDevice ) ) )
return hr;
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: InvalidateDeviceObjects()
// Desc:
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::InvalidateDeviceObjects()
{
m_pFont->InvalidateDeviceObjects();
m_pFontSmall->InvalidateDeviceObjects();
m_pParticleSystem->InvalidateDeviceObjects();
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: DeleteDeviceObjects()
// Desc: Called when the app is exiting, or the device is being changed,
// this function deletes any device dependent objects.
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::DeleteDeviceObjects()
{
m_pFont->DeleteDeviceObjects();
m_pFontSmall->DeleteDeviceObjects();
SAFE_RELEASE( m_pGroundTexture );
SAFE_RELEASE( m_pParticleTexture );
SAFE_RELEASE( m_pGroundVB );
SAFE_RELEASE( m_pGroundIB );
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: FinalCleanup()
// Desc: Called before the app exits, this function gives the app the chance
// to cleanup after itself.
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::FinalCleanup()
{
SAFE_DELETE( m_pFont );
SAFE_DELETE( m_pFontSmall );
SAFE_DELETE( m_pGroundTexture );
SAFE_DELETE( m_pParticleTexture );
SAFE_DELETE( m_pParticleSystem );
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: ConfirmDevice()
// Desc: Called during device intialization, this code checks the device
// for some minimum set of capabilities
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::ConfirmDevice( D3DCAPS8* pCaps, DWORD dwBehavior,
D3DFORMAT Format )
{
// Make sure device can do ONE:ONE alphablending
if( 0 == ( pCaps->SrcBlendCaps & D3DPBLENDCAPS_ONE ) )
return E_FAIL;
if( 0 == ( pCaps->DestBlendCaps & D3DPBLENDCAPS_ONE ) )
return E_FAIL;
// Make sure HW TnL devices can do point sprites
if( (dwBehavior & D3DCREATE_HARDWARE_VERTEXPROCESSING ) ||
(dwBehavior & D3DCREATE_MIXED_VERTEXPROCESSING ) )
{
if( pCaps->MaxPointSize <= 1.0f )
return E_FAIL;
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: MsgProc()
// Desc: Message proc function to handle key and menu input
//-----------------------------------------------------------------------------
LRESULT CMyD3DApplication::MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam )
{
// Record key presses
if( WM_KEYDOWN == uMsg )
{
m_bKey[wParam] = 1;
}
// Perform commands when keys are rleased
if( WM_KEYUP == uMsg )
{
m_bKey[wParam] = 0;
switch( wParam )
{
case VK_F1:
m_bDrawHelp = !m_bDrawHelp;
return 1;
case 'R':
if( m_bCanDoAlphaBlend )
m_bDrawReflection = !m_bDrawReflection;
break;
case VK_F3:
m_bAnimateEmitter = !m_bAnimateEmitter;
break;
case VK_F4:
if( ++m_dwParticleColor >= NUM_COLORS )
m_dwParticleColor = COLOR_WHITE;
break;
}
}
return CD3DApplication::MsgProc( hWnd, uMsg, wParam, lParam );
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
CParticleSystem::CParticleSystem( DWORD dwFlush, DWORD dwDiscard, float fRadius )
{
m_fRadius = fRadius;
m_dwBase = dwDiscard;
m_dwFlush = dwFlush;
m_dwDiscard = dwDiscard;
m_dwParticles = 0;
m_dwParticlesLim = 2048;
m_pParticles = NULL;
m_pParticlesFree = NULL;
m_pVB = NULL;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
CParticleSystem::~CParticleSystem()
{
InvalidateDeviceObjects();
while( m_pParticles )
{
PARTICLE* pSpark = m_pParticles;
m_pParticles = pSpark->m_pNext;
delete pSpark;
}
while( m_pParticlesFree )
{
PARTICLE *pSpark = m_pParticlesFree;
m_pParticlesFree = pSpark->m_pNext;
delete pSpark;
}
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CParticleSystem::RestoreDeviceObjects( LPDIRECT3DDEVICE8 pd3dDevice )
{
HRESULT hr;
// Create a vertex buffer for the particle system. The size of this buffer
// does not relate to the number of particles that exist. Rather, the
// buffer is used as a communication channel with the device.. we fill in
// a bit, and tell the device to draw. While the device is drawing, we
// fill in the next bit using NOOVERWRITE. We continue doing this until
// we run out of vertex buffer space, and are forced to DISCARD the buffer
// and start over at the beginning.
if(FAILED(hr = pd3dDevice->CreateVertexBuffer( m_dwDiscard *
sizeof(POINTVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY | D3DUSAGE_POINTS,
D3DFVF_POINTVERTEX, D3DPOOL_DEFAULT, &m_pVB )))
{
return E_FAIL;
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CParticleSystem::InvalidateDeviceObjects()
{
SAFE_RELEASE( m_pVB );
return S_OK;
}
//-----------------------------------------------------------------------------
// Name:
// Desc:
//-----------------------------------------------------------------------------
HRESULT CParticleSystem::Update( FLOAT fSecsPerFrame, DWORD dwNumParticlesToEmit,
const D3DXCOLOR &clrEmitColor,
const D3DXCOLOR &clrFadeColor, float fEmitVel,
D3DXVECTOR3 vPosition )
{
PARTICLE *pParticle, **ppParticle;
static float fTime = 0.0f;
fTime += fSecsPerFrame;
ppParticle = &m_pParticles;
while( *ppParticle )
{
pParticle = *ppParticle;
// Calculate new position
float fT = fTime - pParticle->m_fTime0;
float fGravity;
if( pParticle->m_bSpark )
{
fGravity = -5.0f;
pParticle->m_fFade -= fSecsPerFrame * 2.25f;
}
else
{
fGravity = -9.8f;
pParticle->m_fFade -= fSecsPerFrame * 0.25f;
}
pParticle->m_vPos = pParticle->m_vVel0 * fT + pParticle->m_vPos0;
pParticle->m_vPos.y += (0.5f * fGravity) * (fT * fT);
pParticle->m_vVel.y = pParticle->m_vVel0.y + fGravity * fT;
if( pParticle->m_fFade < 0.0f )
pParticle->m_fFade = 0.0f;
// Kill old particles
if( pParticle->m_vPos.y < m_fRadius ||
pParticle->m_bSpark && pParticle->m_fFade <= 0.0f )
{
// Emit sparks
if( !pParticle->m_bSpark )
{
for( int i=0; i<4; i++ )
{
PARTICLE *pSpark;
if( m_pParticlesFree )
{
pSpark = m_pParticlesFree;
m_pParticlesFree = pSpark->m_pNext;
}
else
{
if( NULL == ( pSpark = new PARTICLE ) )
return E_OUTOFMEMORY;
}
pSpark->m_pNext = pParticle->m_pNext;
pParticle->m_pNext = pSpark;
pSpark->m_bSpark = TRUE;
pSpark->m_vPos0 = pParticle->m_vPos;
pSpark->m_vPos0.y = m_fRadius;
FLOAT fRand1 = ((FLOAT)rand()/(FLOAT)RAND_MAX) * D3DX_PI * 2.00f;
FLOAT fRand2 = ((FLOAT)rand()/(FLOAT)RAND_MAX) * D3DX_PI * 0.25f;
pSpark->m_vVel0.x = pParticle->m_vVel.x * 0.25f + cosf(fRand1) * sinf(fRand2);
pSpark->m_vVel0.z = pParticle->m_vVel.z * 0.25f + sinf(fRand1) * sinf(fRand2);
pSpark->m_vVel0.y = cosf(fRand2);
pSpark->m_vVel0.y *= ((FLOAT)rand()/(FLOAT)RAND_MAX) * 1.5f;
pSpark->m_vPos = pSpark->m_vPos0;
pSpark->m_vVel = pSpark->m_vVel0;
D3DXColorLerp( &pSpark->m_clrDiffuse, &pParticle->m_clrFade,
&pParticle->m_clrDiffuse, pParticle->m_fFade );
pSpark->m_clrFade = D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f);
pSpark->m_fFade = 1.0f;
pSpark->m_fTime0 = fTime;
}
}
// Kill particle
*ppParticle = pParticle->m_pNext;
pParticle->m_pNext = m_pParticlesFree;
m_pParticlesFree = pParticle;
if(!pParticle->m_bSpark)
m_dwParticles--;
}
else
{
ppParticle = &pParticle->m_pNext;
}
}
// Emit new particles
DWORD dwParticlesEmit = m_dwParticles + dwNumParticlesToEmit;
while( m_dwParticles < m_dwParticlesLim && m_dwParticles < dwParticlesEmit )
{
if( m_pParticlesFree )
{
pParticle = m_pParticlesFree;
m_pParticlesFree = pParticle->m_pNext;
}
else
{
if( NULL == ( pParticle = new PARTICLE ) )
return E_OUTOFMEMORY;
}
pParticle->m_pNext = m_pParticles;
m_pParticles = pParticle;
m_dwParticles++;
// Emit new particle
FLOAT fRand1 = ((FLOAT)rand()/(FLOAT)RAND_MAX) * D3DX_PI * 2.0f;
FLOAT fRand2 = ((FLOAT)rand()/(FLOAT)RAND_MAX) * D3DX_PI * 0.25f;
pParticle->m_bSpark = FALSE;
pParticle->m_vPos0 = vPosition + D3DXVECTOR3( 0.0f, m_fRadius, 0.0f );
pParticle->m_vVel0.x = cosf(fRand1) * sinf(fRand2) * 2.5f;
pParticle->m_vVel0.z = sinf(fRand1) * sinf(fRand2) * 2.5f;
pParticle->m_vVel0.y = cosf(fRand2);
pParticle->m_vVel0.y *= ((FLOAT)rand()/(FLOAT)RAND_MAX) * fEmitVel;
pParticle->m_vPos = pParticle->m_vPos0;
pParticle->m_vVel = pParticle->m_vVel0;
pParticle->m_clrDiffuse = clrEmitColor;
pParticle->m_clrFade = clrFadeColor;
pParticle->m_fFade = 1.0f;
pParticle->m_fTime0 = fTime;
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: Render()
// Desc: Renders the particle system using either pointsprites (if supported)
// or using 4 vertices per particle
//-----------------------------------------------------------------------------
HRESULT CParticleSystem::Render( LPDIRECT3DDEVICE8 pd3dDevice )
{
HRESULT hr;
// Set the render states for using point sprites
pd3dDevice->SetRenderState( D3DRS_POINTSPRITEENABLE, TRUE );
pd3dDevice->SetRenderState( D3DRS_POINTSCALEENABLE, TRUE );
pd3dDevice->SetRenderState( D3DRS_POINTSIZE, FtoDW(0.08f) );
pd3dDevice->SetRenderState( D3DRS_POINTSIZE_MIN, FtoDW(0.00f) );
pd3dDevice->SetRenderState( D3DRS_POINTSCALE_A, FtoDW(0.00f) );
pd3dDevice->SetRenderState( D3DRS_POINTSCALE_B, FtoDW(0.00f) );
pd3dDevice->SetRenderState( D3DRS_POINTSCALE_C, FtoDW(1.00f) );
// Set up the vertex buffer to be rendered
pd3dDevice->SetStreamSource( 0, m_pVB, sizeof(POINTVERTEX) );
pd3dDevice->SetVertexShader( D3DFVF_POINTVERTEX );
PARTICLE* pParticle = m_pParticles;
POINTVERTEX* pVertices;
DWORD dwNumParticlesToRender = 0;
// Lock the vertex buffer. We fill the vertex buffer in small
// chunks, using D3DLOCK_NOOVERWRITE. When we are done filling
// each chunk, we call DrawPrim, and lock the next chunk. When
// we run out of space in the vertex buffer, we start over at
// the beginning, using D3DLOCK_DISCARD.
m_dwBase += m_dwFlush;
if(m_dwBase >= m_dwDiscard)
m_dwBase = 0;
if(FAILED(hr = m_pVB->Lock(m_dwBase * sizeof(POINTVERTEX), m_dwFlush * sizeof(POINTVERTEX),
(BYTE **) &pVertices, m_dwBase ? D3DLOCK_NOOVERWRITE : D3DLOCK_DISCARD)))
{
return hr;
}
// Render each particle
while( pParticle )
{
D3DXVECTOR3 vPos(pParticle->m_vPos);
D3DXVECTOR3 vVel(pParticle->m_vVel);
FLOAT fLengthSq = D3DXVec3LengthSq(&vVel);
UINT dwSteps;
if( fLengthSq < 1.0f ) dwSteps = 2;
else if( fLengthSq < 4.00f ) dwSteps = 3;
else if( fLengthSq < 9.00f ) dwSteps = 4;
else if( fLengthSq < 12.25f ) dwSteps = 5;
else if( fLengthSq < 16.00f ) dwSteps = 6;
else if( fLengthSq < 20.25f ) dwSteps = 7;
else dwSteps = 8;
vVel *= -0.04f / (FLOAT)dwSteps;
D3DXCOLOR clrDiffuse;
D3DXColorLerp(&clrDiffuse, &pParticle->m_clrFade, &pParticle->m_clrDiffuse, pParticle->m_fFade);
DWORD dwDiffuse = (DWORD) clrDiffuse;
// Render each particle a bunch of times to get a blurring effect
for( DWORD i = 0; i < dwSteps; i++ )
{
pVertices->v = vPos;
pVertices->color = dwDiffuse;
pVertices++;
if( ++dwNumParticlesToRender == m_dwFlush )
{
// Done filling this chunk of the vertex buffer. Lets unlock and
// draw this portion so we can begin filling the next chunk.
m_pVB->Unlock();
if(FAILED(hr = pd3dDevice->DrawPrimitive( D3DPT_POINTLIST, m_dwBase, dwNumParticlesToRender)))
return hr;
// Lock the next chunk of the vertex buffer. If we are at the
// end of the vertex buffer, DISCARD the vertex buffer and start
// at the beginning. Otherwise, specify NOOVERWRITE, so we can
// continue filling the VB while the previous chunk is drawing.
m_dwBase += m_dwFlush;
if(m_dwBase >= m_dwDiscard)
m_dwBase = 0;
if(FAILED(hr = m_pVB->Lock(m_dwBase * sizeof(POINTVERTEX), m_dwFlush * sizeof(POINTVERTEX),
(BYTE **) &pVertices, m_dwBase ? D3DLOCK_NOOVERWRITE : D3DLOCK_DISCARD)))
{
return hr;
}
dwNumParticlesToRender = 0;
}
vPos += vVel;
}
pParticle = pParticle->m_pNext;
}
// Unlock the vertex buffer
m_pVB->Unlock();
// Render any remaining particles
if( dwNumParticlesToRender )
{
if(FAILED(hr = pd3dDevice->DrawPrimitive( D3DPT_POINTLIST, m_dwBase, dwNumParticlesToRender )))
return hr;
}
// Reset render states
pd3dDevice->SetRenderState( D3DRS_POINTSPRITEENABLE, FALSE );
pd3dDevice->SetRenderState( D3DRS_POINTSCALEENABLE, FALSE );
return S_OK;
}